﻿#NoEnv
#SingleInstance Force
SetBatchLines, -1
Process, Priority,, High
SetWorkingDir %A_ScriptDir%

;=================================================================================================
;================================管理员身份检查与处理================================================
If !A_IsAdmin {
	Try
	{
		If A_IsCompiled {
			Run *RunAs "%A_ScriptFullPath%" /restart
			ExitApp
		}else{
			Run *RunAs "%A_AhkPath%" /restart "%A_ScriptFullPath%"
		}
	}Catch e{
		MsgBox, 262160,Error,% e.Extra "`n以管理员身份运行失败！",15
		ExitApp
	}
}










;=================================================================================================
;================================托盘菜单==========================================================
Menu, TRAY, NoStandard
Menu, TRAY, DeleteAll
Menu, Tray, UseErrorLevel
Menu, TRAY,Add, WinSpy,WindowSpy
Menu, TRAY,Add,
Menu, TRAY,Add, 任務窗口,SetAutoTask
Menu, TRAY,Add,
Menu, TRAY,Add, 重啓程序,OnReload
Menu, TRAY,Add,
Menu, TRAY,Add, 退出程序,OnExits
Menu, TRAY,Tip,簡易自啓計劃任務工具
if !A_IsCompiled
	Menu, TRAY,Icon,shell32.dll,261

;=================================================================================================
;================================重复进程检查与处理================================================
If A_IsCompiled {
	thisPID := DllCall("GetCurrentProcessId")
	if objCount(Processlist:=GetProcessNameList(A_ScriptName))>1
	{
		loop,% objCount(Processlist)
		{
			if (thisPID<>Processlist[A_Index,2]){
				process,Close,% Processlist[A_Index,2]
			}
		}
	}
}Else{
	DetectHiddenWindows, On
	thisPID := DllCall("GetCurrentProcessId")
	WinGet, List, List, %A_ScriptFullPath% ahk_class AutoHotkey
	Loop % List
	{ 
		WinGet, PID, PID, % "ahk_id" List%A_Index%
		If (PID != thisPID){
			Process, Close, %PID%
		}
	}
	DetectHiddenWindows, Off
}

;;=======================================================================================
;;================================程序图标如果隐藏托盘则提醒=================================
TrayIconInfo:=TrayIcon_GetInfo(A_IsCompiled?A_ScriptName:RegExReplace(A_AhkPath,".+\\"))
if objcount(TrayIconInfo){
	for key,value In TrayIconInfo
	{
		if (InStr(TrayIconInfo[key,"Tray"],"NotifyIconOverflowWindow")&&TrayIconInfo[key,"pid"]=DllCall("GetCurrentProcessId")){
			if !A_IconHidden
				Traytip,Warning Notify,请把程序图标从托盘区移至`n任务栏以方便更好的操作！,,1
			Else
				MsgBox, 262144, Warning Notify,请把程序图标从托盘区移至`n任务栏以方便更好的操作！,10
		}
	}
}

AutoTask_Warning=
(
;;自启不成功可能是杀软拦截了或系统Task Scheduler服务项被禁用了。
;;ahk脚本自启过快任务栏无图标解决方法：
;;1、设置自启延迟。
;;2、添加自启时获取任务栏所有图标信息判断存不存在，不存在就重启脚本直到出现图标为步。建议用第二种稳妥
;;ahk脚本或编译文件检查开机自启后丢失托盘图标并处理
TrayIconInfo:=TrayIcon_GetInfo(A_IsCompiled?A_ScriptName:RegExReplace(A_AhkPath,".+\\"))
if (!A_IconHidden) {
	if (A_IsCompiled&&!objcount(TrayIconInfo)){
		OnReload()
	}else if (!A_IsCompiled&&!objcount(TrayIconInfo)){
		OnReload()
	}else if (!A_IsCompiled&&objcount(TrayIconInfo)){
		IsNoHide:=0
		for key,value In TrayIconInfo
		{
			if (value.pid=DllCall("GetCurrentProcessId"))
				IsNoHide++
		}
		if !IsNoHide
			OnReload()
	}
}

OnReload(){
	Reload
}

;;;根据进程文件名获取托盘图标信息列表，以判断托盘图标是否正常显示
TrayIcon_GetInfo(sExeName := "")
{
	d := A_DetectHiddenWindows
	DetectHiddenWindows, On
 
	oTrayInfo := []
	For key,sTray in ["Shell_TrayWnd", "NotifyIconOverflowWindow"]
	{
		idxTB := TrayIcon_GetTrayBar(sTray)
		WinGet, pidTaskbar, PID, ahk_class %sTray%
		
		hProc := DllCall("OpenProcess",	UInt,0x38, Int,0, UInt,pidTaskbar)
		pRB   := DllCall("VirtualAllocEx", Ptr,hProc, Ptr,0, UPtr,20, UInt,0x1000, UInt,0x04)
 
		szBtn := VarSetCapacity(btn, (A_Is64bitOS ? 32 : 20), 0)
		szNfo := VarSetCapacity(nfo, (A_Is64bitOS ? 32 : 24), 0)
		szTip := VarSetCapacity(tip, 128 * 2, 0)
 
		; TB_BUTTONCOUNT = 0x0418
		SendMessage, 0x0418, 0, 0, ToolbarWindow32%idxTB%, ahk_class %sTray%
		Loop, %ErrorLevel%
		{
			 ; TB_GETBUTTON 0x0417
			SendMessage, 0x0417, A_Index-1, pRB, ToolbarWindow32%idxTB%, ahk_class %sTray%
 
			DllCall("ReadProcessMemory", Ptr,hProc, Ptr,pRB, Ptr,&btn, UPtr,szBtn, UPtr,0)
 
			iBitmap := NumGet(btn, 0, "Int")
			idCmd   := NumGet(btn, 4, "Int")
			fsState := NumGet(btn, 8, "UChar")
			fsStyle := NumGet(btn, 9, "UChar")
			dwData  := NumGet(btn, (A_Is64bitOS ? 16 : 12), "UPtr")
			iString := NumGet(btn, (A_Is64bitOS ? 24 : 16), "Ptr")
 
			DllCall("ReadProcessMemory", Ptr,hProc, Ptr,dwData, Ptr,&nfo, UPtr,szNfo, UPtr,0)
 
			hWnd  := NumGet(nfo, 0, "Ptr")
			uId   := NumGet(nfo, (A_Is64bitOS ?  8 :  4), "UInt")
			msgId := NumGet(nfo, (A_Is64bitOS ? 12 :  8), "UPtr")
			hIcon := NumGet(nfo, (A_Is64bitOS ? 24 : 20), "Ptr")
 
			WinGet, nPid, PID, ahk_id %hWnd%
			WinGet, sProcess, ProcessName, ahk_id %hWnd%
			WinGetClass, sClass, ahk_id %hWnd%
 
			If ( !sExeName || sExeName == sProcess || sExeName == nPid )
			{
				DllCall("ReadProcessMemory", Ptr,hProc, Ptr,iString, Ptr,&tip, UPtr,szTip, UPtr,0)
				oTrayInfo.Push({ "idx"	 : A_Index-1
							   , "idcmd"   : idCmd
							   , "pid"	 : nPid
							   , "uid"	 : uId
							   , "msgid"   : msgId
							   , "hicon"   : hIcon
							   , "hwnd"	: hWnd
							   , "class"   : sClass
							   , "process" : sProcess
							   , "tooltip" : StrGet(&tip, "UTF-16")
							   , "tray"	: sTray })
			}
		}
		DllCall("VirtualFreeEx", Ptr,hProc, Ptr,pRB, UPtr,0, UInt,0x8000)
		DllCall("CloseHandle",   Ptr,hProc)
	}
	DetectHiddenWindows, %d%
	Return oTrayInfo
}

TrayIcon_GetTrayBar(sTray:="Shell_TrayWnd")
{
	d := A_DetectHiddenWindows
	DetectHiddenWindows, On
	WinGet, ControlList, ControlList, ahk_class %sTray%
	RegExMatch(ControlList, "(?<=ToolbarWindow32)\d+(?!.*ToolbarWindow32)", nTB)
	Loop, %nTB%
	{
		ControlGet, hWnd, hWnd,, ToolbarWindow32%A_Index%, ahk_class %sTray%
		hParent := DllCall( "GetParent", Ptr, hWnd )
		WinGetClass, sClass, ahk_id %hParent%
		If !(sClass == "SysPager" || sClass == "NotifyIconOverflowWindow" )
			Continue
		idxTB := A_Index
		Break
	}
	DetectHiddenWindows, %d%
	Return idxTB
}

)

TrayRefresh(),EmptyMem()

;;===================================================================================================
;;================================监控消息回调ShellIMEMessage监控窗口变化================================
Gui +LastFound
DllCall( "RegisterShellHookWindow", UInt,WinExist() )
OnMessage( DllCall( "RegisterWindowMessage", Str,"SHELLHOOK" ), "ShellIMEMessage")
ShellIMEMessage( wParam,lParam ) {
	/*    wParam参数：
		;1 顶级窗体被创建 	;2 顶级窗体即将被关闭 	;54 退出全屏	;32772 窗口切换
		;3 SHELL 的主窗体将被激活 	;4 顶级窗体被激活 	;&H8000& 掩码 	;53 全屏
		;5 顶级窗体被最大化或最小化 	;6 Windows 任务栏被刷新，也可以理解成标题变更
		;7 任务列表的内容被选中 	;8 中英文切换或输入法切换 	;13 wParam=被替换的顶级窗口的hWnd 
		;9 显示系统菜单 	;10 顶级窗体被强制关闭 	;14 wParam=替换顶级窗口的窗口hWnd
		;12 没有被程序处理的APPCOMMAND。见WM_APPCOMMAND
	*/
	if (A_Is64bitOS&&A_PtrSize<8){
		SetRegView, 64
	}
	RegRead, InstallDir, HKEY_LOCAL_MACHINE\SOFTWARE\AutoHotkey,InstallDir
	if FileExist(InstallDir "\WindowSpy.ahk"){
		Menu, TRAY,Enable, WinSpy
	}else{
		Menu, TRAY,Disable, WinSpy
	}
	;消除重复运行程序时弹出”Could not close the previous instance of this script. Keep waiting“窗口
	if WinExist(A_ScriptName){
		ControlGetText, WinStaticText , Static1,A
		if WinStaticText {
			if InStr(WinStaticText,"keep Waiting"){
				WinSet,AlwaysOnTop,On,A
				WinActivate ,A
				ControlFocus,Button1
				ControlSend , Button1, {Enter}, A
				if ErrorLevel {
					Send,{Enter}
				}
				OnReload()
			}
		}
	}

}

;;==================================================================================================================
;;==================================================================================================================
;;==================================================================================================================
;;==================================================================================================================

WindowSpy(){
	RegRead, InstallDir, HKEY_LOCAL_MACHINE\SOFTWARE\AutoHotkey,InstallDir
	if FileExist(InstallDir "\WindowSpy.ahk"){
		if A_IsCompiled {
			Run *RunAs "%InstallDir%\AutoHotkey.exe" "%InstallDir%\WindowSpy.ahk" /restart
		}Else
			Run *RunAs "%A_AhkPath%" /restart "%InstallDir%\WindowSpy.ahk"
	}
}

OnReload(){
	Reload
}


OnExits(){
	ExitApp
}

SetAutoTask:
	if A_ThisMenuItemPos {
		Gui,AutoTask:Destroy
		Gui,AutoTask:Default
		Gui,AutoTask:+HWNDSetAutoTask
		Gui,AutoTask:Font,s10
		Gui,AutoTask:Color,ffffff
		Gui,AutoTask:margin,15,15
		Gui,AutoTask:Add,ListView, r12 w600 AltSubmit -LV0x10 -Multi NoSortHdr -WantF2 Grid ggetTaskList vTaskList,任务名称|启动文件|附加参数/脚本路径|任务状态
		Gui,AutoTask:Add,Button,gaddTask,新增任務
		Gui,AutoTask:Add,Button,x+10 yp gReloadList,刷新列表
		Gui,AutoTask:Add,StatusBar,,
		Gui,AutoTask:Show,NA AutoSize,AutoTask
		ChangeWindowIcon("shell32.dll", WinExist("ahk_id " SetAutoTask), 166)
		SysGet, SM_CXVSCROLL, 2 
		LV_ModifyCol(1,300),LV_ModifyCol(2,400),LV_ModifyCol(3,400),LV_ModifyCol(4,"Center " (100-(A_ScreenDPI/96)*SM_CXVSCROLL))
		if objCount(TaskInfos:=GetTaskInfos()){
			for Section,element In TaskInfos
			{
				TaskPATH:=StrSplit(element,"=")
				for key,value In GetTaskActionExecPaths(TaskPATH[1])
					LV_Add(key,TaskPATH[1],value[1],value[2],TaskPATH[2])
			}
		}
	}
Return

getTaskList:
	EventInfo:=0
	if (A_GuiEvent ="RightClick"){
		EventInfo:=A_EventInfo
	}
Return

AutoTaskGuiContextMenu(GuiHwnd, CtrlHwnd, EventInfo, IsRightClick, X, Y){
	if (IsRightClick&&A_GuiControl="TaskList"&&EventInfo) {
		Menu,ControlTask,Delete
		Menu,ControlTask,Add,刷新列表,ReloadList
		Menu,ControlTask,Add,
		Menu,ControlTask,Add,刪除任務,DeleteItem
		Menu, ControlTask,Show
	}
}

ReloadList:
	Gui,AutoTask:Default
	LV_Delete()
	if objCount(TaskInfos:=GetTaskInfos()){
		for Section,element In TaskInfos
		{
			TaskPATH:=StrSplit(element,"=")
			for key,value In GetTaskActionExecPaths(TaskPATH[1])
				LV_Add(key,TaskPATH[1],value[1],value[2],TaskPATH[2])
		}
	}
Return

DeleteItem:
	if (EventInfo){
		Gui,AutoTask:Default
		LV_GetText(tname, EventInfo)
		if tname {
			if DisableAutorun(tname){
				LV_Delete(EventInfo)
			}
		}
	}
Return

addTask:
	Gui,NewTask:Destroy
	Gui,NewTask:Default
	Gui,NewTask:+OwnerAutoTask -MinimizeBox HWNDAddNewTask
	Gui,NewTask:Font,s10
	Gui,NewTask:Color,ffffff,ffffff
	Gui,NewTask:Add,GroupBox,y+15 w350 h200 vGBox1,任務信息
	GuiControlGet,GBox1Pos,NewTask:Pos,GBox1
	Gui,NewTask:Add,Text,% "x" GBox1PosX+15 " y" GBox1PosY+30,任務名稱：
	Gui,NewTask:Add,Edit,x+5 yp vSetTaskName HWNDTaskNameEdit,
	EM_SetCueBanner(TaskNameEdit, "任務名稱必填！")
	Gui,NewTask:Add,Text,% "x" GBox1PosX+15 " y+15",任務簡介：
	Gui,NewTask:Add,Edit,x+5 yp r3 vTaskDescription
	Gui,NewTask:Add,Text,% "x" GBox1PosX+15 " y+20" ,啓動文件：`n路徑
	Gui,NewTask:Add,Edit,x+5 yp r3 vProcessPath HWNDSetProcessPath,
	EM_SetCueBanner(SetProcessPath, "啓動文件路徑必填！")
	Gui,NewTask:Add,Button,x+10 yp gSelectProcessPath,選擇
	Gui,NewTask:Add,Button,xp y+5 gGetProcessPath,識別
	Gui,NewTask:Add,Text,% "x" GBox1PosX+15 " y+20",附加參數：`n腳本路徑
	Gui,NewTask:Add,Edit,x+5 yp r2 vScriptPath,
	Gui,NewTask:Add,Button,x+10 yp gSelectScriptPath vSelectScriptPath,選擇`n路徑
	GuiControlGet,SPathPos,NewTask:Pos,SelectScriptPath
	GuiControl,NewTask:Move,GBox1,% "h" SPathPosY-GBox1PosY+SPathPosH+20 " w" SPathPosX-GBox1PosX+SPathPosW+30
	Gui,NewTask:Add,Button,% "xm+12 y" SPathPosY+SPathPosH+30 " w" (SPathPosX-GBox1PosX+SPathPosW)/2 " gSubmitTask" ,提交任務
	Gui,NewTask:Add,Button,% "x+5 y" SPathPosY+SPathPosH+30 " w" (SPathPosX-GBox1PosX+SPathPosW)/2 " gTaskWarning" ,自啓異常？
	Gui,NewTask:Font,s9
	Gui,NewTask:Add,StatusBar,  ;-Theme Backgroundffffff
	Gui,NewTask:Show,NA AutoSize,New Task
	if !A_IsCompiled {
		ChangeWindowIcon("shell32.dll", WinExist("ahk_id " AddNewTask), 261)
	}
Return

SelectProcessPath:
	Gui,+OwnDialogs
	FileSelectFile, filepath, 3, , 請選擇要自啓的EXE程序文件, ProgramFile (*.exe)
	if (filepath <> ""){
		FileDescription:=FileGetInfo(filepath,"FileDescription")
		GuiControlGet,SetTaskName,,SetTaskName,Text
		if (!SetTaskName&&FileDescription)
			GuiControl,NewTask:,SetTaskName,% FileDescription
		GuiControl,NewTask:,ProcessPath,% filepath
	}
Return

TaskWarning:
	FileDelete,%A_Desktop%\ahk脚本自启无图标解决方法.txt
	FileAppend,%AutoTask_Warning%,%A_Desktop%\ahk脚本自启无图标解决方法.txt,utf-8
	Run, %A_WinDir%\notepad.exe  %A_Desktop%\ahk脚本自启无图标解决方法.txt,, UseErrorLevel
Return

SelectScriptPath:
	Gui,+OwnDialogs
	FileSelectFile, filepath, 3, , 請選擇脚本文件, Script File (*.ahk)
	if (filepath <> ""){
		GuiControl,NewTask:,ScriptPath,% filepath
	}
Return

GetProcessPath:
	SB_SetText("請在15秒内把鼠標指針放在目標窗口上,按LCtrl確認！")
	keywait, LControl, D T15
	keywait, LControl
	MouseGetPos, , , id
	WinGet, win_exe, ProcessPath, ahk_id %id%
	If win_exe {
		FileDescription:=FileGetInfo(win_exe,"FileDescription")
		GuiControlGet,SetTaskName,,SetTaskName,Text
		if (!SetTaskName&&FileDescription)
			GuiControl,NewTask:,SetTaskName,% FileDescription
		GuiControl,NewTask:,ProcessPath,% win_exe
	}
	SB_SetText("")
Return

SubmitTask:
	Gui,NewTask:Submit,NoHide
	if (FileExist(ProcessPath)&&SetTaskName) {
		if IsAutorunEnabled(SetTaskName){
			Gui,+OwnDialogs
			MsgBox, 262160, Error,任務「%SetTaskName%」已存在！,8
			Return
		}
		flag:=EnableAutoRun(SetTaskName,ProcessPath,ScriptPath,TaskDescription)
		if (flag=SetTaskName){
			Gui,AutoTask:Default
			LV_Add(LV_GetCount(),SetTaskName,ProcessPath,ScriptPath,"Ready")
			;GuiControl,NewTask:,SetTaskName,
			;GuiControl,NewTask:,ProcessPath,
			;GuiControl,NewTask:,ScriptPath,
			Gui,NewTask:Destroy
		}Else if (flag=1){
			MsgBox, 262160, Error,權限不夠，添加失敗！,8
			Return
		}Else if (flag<0){
			MsgBox, 262160, Error,任務名稱和啓動文件路徑不能爲空！,8
			Return
		}
	}
Return

EM_SetCueBanner(hWnd, Cue)
{
	static EM_SETCUEBANNER := 0x1501
	return DllCall("User32.dll\SendMessage", "Ptr", hWnd, "UInt", EM_SETCUEBANNER, "Ptr", True, "WStr", Cue)
}

;获取系统计划任务自启列表
GetTaskInfos() {
	objService := ComObjCreate("Schedule.Service")
	objService.Connect()
	rootFolder := objService.GetFolder("\")
	taskCollection := rootFolder.GetTasks(0)
	numberOfTasks := taskCollection.Count
	; ?RegistrationInfo.Author
	For registeredTask, state in taskCollection
	{
		if (registeredTask.state == 0)
			state:= "Unknown"
		else if (registeredTask.state == 1)
			state:= "Disabled"
		else if (registeredTask.state == 2)
			state:= "Queued"
		else if (registeredTask.state == 3)
			state:= "Ready"
		else if (registeredTask.state == 4)
			state:= "Running"
		tasklist .= registeredTask.Name "=" state "=" registeredTask.state "`n"
	}
	return StrSplit(RTrim(tasklist,"`n"),"`n")
}

;判断是否自启
IsAutorunEnabled(TaskName)
{
	objService := ComObjCreate("Schedule.Service") 
	objService.Connect()
	objFolder := objService.GetFolder("\")
	Try {
		RunAsTask := objFolder.GetTask( TaskName )
		return  ! A_LastError 
	}Catch
		Return ""
}

;取消自启
DisableAutorun(TaskName){
	objService := ComObjCreate("Schedule.Service") 
	objService.Connect()
	objFolder := objService.GetFolder("\")
	Try {
		objFolder.DeleteTask(TaskName, 0)
		Return !A_LastError
	}Catch
		Return ""
}

;设置自启
EnableAutoRun(taskName:="",ProcessPath="",ScriptPath="",DescriptionInfo:="",TaskState=3)
{
	if (!TaskName||!ProcessPath)
		Return -1
	TaskExists := IsAutorunEnabled(taskName)
	if (A_IsAdmin and TaskExists) {
		if DisableAutorun(TaskName)
			Return 1
	}else If ( not A_IsAdmin and TaskExists ) {
		Return ""
	}else If ( not A_IsAdmin and not TaskExists ){
		Return ""
	}else If ( A_IsAdmin and not TaskExists ) {
		TriggerType = 9   ; trigger on logon. 
		ActionTypeExec = 0  ; specifies an executable action. 
		TaskCreateOrUpdate = 6 
		Task_Runlevel_Highest = 1 

		objService := ComObjCreate("Schedule.Service") 
		objService.Connect() 

		objFolder := objService.GetFolder("\") 
		objTaskDefinition := objService.NewTask(0) 

		principal := objTaskDefinition.Principal 
		principal.LogonType := 1    ; Set the logon type to TASK_LOGON_PASSWORD 
		principal.RunLevel := Task_Runlevel_Highest  ; Tasks will be run with the highest privileges. 

		colTasks := objTaskDefinition.Triggers 
		objTrigger := colTasks.Create(TriggerType) 
		colActions := objTaskDefinition.Actions 
		objAction := colActions.Create(ActionTypeExec) 
		objAction.ID := "Autorun" 
		if(!FileExist(ScriptPath)&&FileExist(ProcessPath)){
			objAction.Path := """" ProcessPath """"
		}else if(FileExist(ScriptPath)&&FileExist(ProcessPath))
		{
			objAction.Path := """" ProcessPath """"
			objAction.Arguments := """" ScriptPath """"
		}Else{
			Return ""
		}
		objAction.WorkingDirectory := RegExReplace(ProcessPath,".+\\")
		objInfo := objTaskDefinition.RegistrationInfo 
		objInfo.Author := A_UserName 
		objInfo.Description := DescriptionInfo
		objSettings := objTaskDefinition.Settings 
		objSettings.Enabled := True 
		objSettings.Hidden := False 
		objSettings.StartWhenAvailable := True 
		objSettings.ExecutionTimeLimit := "PT0S"
		objSettings.DisallowStartIfOnBatteries := False
		objSettings.StopIfGoingOnBatteries := False
		objFolder.RegisterTaskDefinition(taskName, objTaskDefinition, TaskCreateOrUpdate , "", "", TaskState ) 
		if IsAutorunEnabled(taskName)
			Return taskName
	}
}

GetTaskActionExecPaths(taskName, folderPath := "\") {
	service := ComObjCreate("Schedule.Service")
	service.Connect()
	Try {
		Paths:=[]
		for action in service.GetFolder(folderPath).GetTask(taskName).Definition.Actions
			if (action.Type ==0)
				Paths.Push([RegExReplace(action.Path,"[\""""]"),RegExReplace(action.Arguments,"[\""""]")])
		Return Paths
	}Catch
		Return []
}

;;;根据进程文件名获取托盘图标信息列表，以判断托盘图标是否正常显示
TrayIcon_GetInfo(sExeName := "")
{
	d := A_DetectHiddenWindows
	DetectHiddenWindows, On
 
	oTrayInfo := []
	For key,sTray in ["Shell_TrayWnd", "NotifyIconOverflowWindow"]
	{
		idxTB := TrayIcon_GetTrayBar(sTray)
		WinGet, pidTaskbar, PID, ahk_class %sTray%
		
		hProc := DllCall("OpenProcess",	UInt,0x38, Int,0, UInt,pidTaskbar)
		pRB   := DllCall("VirtualAllocEx", Ptr,hProc, Ptr,0, UPtr,20, UInt,0x1000, UInt,0x04)
 
		szBtn := VarSetCapacity(btn, (A_Is64bitOS ? 32 : 20), 0)
		szNfo := VarSetCapacity(nfo, (A_Is64bitOS ? 32 : 24), 0)
		szTip := VarSetCapacity(tip, 128 * 2, 0)
 
		; TB_BUTTONCOUNT = 0x0418
		SendMessage, 0x0418, 0, 0, ToolbarWindow32%idxTB%, ahk_class %sTray%
		Loop, %ErrorLevel%
		{
			 ; TB_GETBUTTON 0x0417
			SendMessage, 0x0417, A_Index-1, pRB, ToolbarWindow32%idxTB%, ahk_class %sTray%
 
			DllCall("ReadProcessMemory", Ptr,hProc, Ptr,pRB, Ptr,&btn, UPtr,szBtn, UPtr,0)
 
			iBitmap := NumGet(btn, 0, "Int")
			idCmd   := NumGet(btn, 4, "Int")
			fsState := NumGet(btn, 8, "UChar")
			fsStyle := NumGet(btn, 9, "UChar")
			dwData  := NumGet(btn, (A_Is64bitOS ? 16 : 12), "UPtr")
			iString := NumGet(btn, (A_Is64bitOS ? 24 : 16), "Ptr")
 
			DllCall("ReadProcessMemory", Ptr,hProc, Ptr,dwData, Ptr,&nfo, UPtr,szNfo, UPtr,0)
 
			hWnd  := NumGet(nfo, 0, "Ptr")
			uId   := NumGet(nfo, (A_Is64bitOS ?  8 :  4), "UInt")
			msgId := NumGet(nfo, (A_Is64bitOS ? 12 :  8), "UPtr")
			hIcon := NumGet(nfo, (A_Is64bitOS ? 24 : 20), "Ptr")
 
			WinGet, nPid, PID, ahk_id %hWnd%
			WinGet, sProcess, ProcessName, ahk_id %hWnd%
			WinGetClass, sClass, ahk_id %hWnd%
 
			If ( !sExeName || sExeName == sProcess || sExeName == nPid )
			{
				DllCall("ReadProcessMemory", Ptr,hProc, Ptr,iString, Ptr,&tip, UPtr,szTip, UPtr,0)
				oTrayInfo.Push({ "idx"	 : A_Index-1
							   , "idcmd"   : idCmd
							   , "pid"	 : nPid
							   , "uid"	 : uId
							   , "msgid"   : msgId
							   , "hicon"   : hIcon
							   , "hwnd"	: hWnd
							   , "class"   : sClass
							   , "process" : sProcess
							   , "tooltip" : StrGet(&tip, "UTF-16")
							   , "tray"	: sTray })
			}
		}
		DllCall("VirtualFreeEx", Ptr,hProc, Ptr,pRB, UPtr,0, UInt,0x8000)
		DllCall("CloseHandle",   Ptr,hProc)
	}
	DetectHiddenWindows, %d%
	Return oTrayInfo
}

TrayIcon_GetTrayBar(sTray:="Shell_TrayWnd")
{
	d := A_DetectHiddenWindows
	DetectHiddenWindows, On
	WinGet, ControlList, ControlList, ahk_class %sTray%
	RegExMatch(ControlList, "(?<=ToolbarWindow32)\d+(?!.*ToolbarWindow32)", nTB)
	Loop, %nTB%
	{
		ControlGet, hWnd, hWnd,, ToolbarWindow32%A_Index%, ahk_class %sTray%
		hParent := DllCall( "GetParent", Ptr, hWnd )
		WinGetClass, sClass, ahk_id %hParent%
		If !(sClass == "SysPager" || sClass == "NotifyIconOverflowWindow" )
			Continue
		idxTB := A_Index
		Break
	}
	DetectHiddenWindows, %d%
	Return idxTB
}
;;根据进程文件名获取进程信息列表
GetProcessNameList(ProcessName:=""){
	list:="",tarr:=[]
	s := 10240  ; 缓存和数组的大小(4 KB)

	Process, Exist  ; 设置 ErrorLevel 为这个正在运行脚本的 PID.
	; 使用 PROCESS_QUERY_INFORMATION(0x0400) 获取此脚本的句柄:
	h := DllCall("OpenProcess", "UInt", 0x0400, "Int", false, "UInt", ErrorLevel, "Ptr")
	; 打开此进程的可调整的访问令牌(TOKEN_ADJUST_PRIVILEGES = 32):
	DllCall("Advapi32.dll\OpenProcessToken", "Ptr", h, "UInt", 32, "PtrP", t)
	VarSetCapacity(ti, 16, 0)  ; 特权结构
	NumPut(1, ti, 0, "UInt")  ; 特权数组中的一个条目...
	; 获取调试特权的本地唯一标识符:
	DllCall("Advapi32.dll\LookupPrivilegeValue", "Ptr", 0, "Str", "SeDebugPrivilege", "Int64P", luid)
	NumPut(luid, ti, 4, "Int64")
	NumPut(2, ti, 12, "UInt")  ; 启用这个特权: SE_PRIVILEGE_ENABLED = 2
	; 使用新的访问令牌更新此进程的特权:
	r := DllCall("Advapi32.dll\AdjustTokenPrivileges", "Ptr", t, "Int", false, "Ptr", &ti, "UInt", 0, "Ptr", 0, "Ptr", 0)
	DllCall("CloseHandle", "Ptr", t)  ; 关闭此访问令牌句柄以节约内存.
	DllCall("CloseHandle", "Ptr", h)  ; 关闭此进程句柄以节约内存.

	hModule := DllCall("LoadLibrary", "Str", "Psapi.dll")  ; 通过预加载来提升性能.
	s := VarSetCapacity(a, s)  ; 接收进程列表标识符的数组:

	DllCall("Psapi.dll\EnumProcesses", "Ptr", &a, "UInt", s, "UIntP", r)
	Loop, % r // 4  ; 把数组解析为 DWORD(32 位) 的标识符:
	{
		id := NumGet(a, A_Index * 4, "UInt")
		; 打开进程: PROCESS_VM_READ(0x0010) | PROCESS_QUERY_INFORMATION(0x0400)
		h := DllCall("OpenProcess", "UInt", 0x0010 | 0x0400, "Int", false, "UInt", id, "Ptr")
		if !h
			continue
		VarSetCapacity(n, s, 0)  ; 接收模块基础名称的缓存:
		e := DllCall("Psapi.dll\GetModuleBaseName", "Ptr", h, "Ptr", 0, "Str", n, "UInt", A_IsUnicode ? s//2 : s)
		if !e    ;  用于 64 位进程在 32 位模式时的回退方法:
			if e := DllCall("Psapi.dll\GetProcessImageFileName", "Ptr", h, "Str", n, "UInt", A_IsUnicode ? s//2 : s)
				SplitPath n, n
		DllCall("CloseHandle", "Ptr", h)  ; 关闭进程句柄以节约内存
		if (n && e){  ; 如果映像不是空的, 则添加到列表:
			If (ProcessName&&ProcessName=n)
				tarr.push([n,id])
			else
				list.=n "/" id "|"
		}
	}
	DllCall("FreeLibrary", "Ptr", hModule)  ; 卸载库来释放内存.

	return !ProcessName?list:tarr
}

EmptyMem(PID=""){
	pid:=!PID ? DllCall("GetCurrentProcessId") : pid
	h:=DllCall("OpenProcess", "UInt", 0x001F0FFF, "Int", 0, "Int", pid)
	DllCall("SetProcessWorkingSetSize", "UInt", h, "Int", -1, "Int", -1)
	DllCall("CloseHandle", "Int", h)
}

TrayRefresh() {
/*		Remove any dead icon from the tray menu
 *		Should work both for W7 & W10
 */
	WM_MOUSEMOVE := 0x200
	detectHiddenWin := A_DetectHiddenWindows
	DetectHiddenWindows, On
	allTitles := ["ahk_class Shell_TrayWnd"
			, "ahk_class NotifyIconOverflowWindow"]
	allControls := ["ToolbarWindow321"
				,"ToolbarWindow322"
				,"ToolbarWindow323"
				,"ToolbarWindow324"]
	allIconSizes := [24,32]

	for id, title in allTitles {
		for id, controlName in allControls
		{
			for id, iconSize in allIconSizes
			{
				ControlGetPos, xTray,yTray,wdTray,htTray,% controlName,% title
				y := htTray - 10
				While (y > 0)
				{
					x := wdTray - iconSize/2
					While (x > 0)
					{
						point := (y << 16) + x
						PostMessage,% WM_MOUSEMOVE, 0,% point,% controlName,% title
						x -= iconSize/2
					}
					y -= iconSize/2
				}
			}
		}
	}
	DetectHiddenWindows, %detectHiddenWin%
}

ChangeWindowIcon(IconFile, hWnd:="", IconNumber:=1, IconSize:=128) {    ;ico图标文件IconNumber和IconSize不用填，如果是icl图标库需要填
	hWnd :=hWnd?hWnd:WinExist("A")
	if (!hWnd)
		return "窗口不存在！"
	if not IconFile~="\.ico$"
		hIcon := LoadIcon(IconFile, IconNumber, IconSize)
	else
		hIcon := DllCall("LoadImage", uint, 0, str, IconFile, uint, 1, int, 0, int, 0, uint, LR_LOADFROMFILE:=0x10)
	if (!hIcon)
		return "图标文件不存在！"
	SendMessage, WM_SETICON:=0x80, ICON_SMALL2:=0, hIcon,, ahk_id %hWnd%  ; Set the window's small icon
	;;;SendMessage, STM_SETICON:=0x0170, hIcon, 0,, Ahk_ID %hWnd%
	SendMessage, WM_SETICON:=0x80, ICON_BIG:=1   , hIcon,, ahk_id %hWnd%  ; Set the window's big icon to the same one.
}


;获取exe/dll/icl文件中指定图标找返回
LoadIcon(Filename, IconNumber, IconSize)
{
	if DllCall("PrivateExtractIcons"
		, "str", Filename, "int", IconNumber-1, "int", IconSize, "int", IconSize
		, "ptr*", hIcon, "uint*", 0, "uint", 1, "uint", 0, "ptr")
		return hIcon
}

/*
	FileDescription	文件描述说明
	FileVersion	文件版本
	InternalName	内部名称
	LegalCopyright	法律版权所有
	OriginalFilename	创建文件时所使用的名称
	ProductName	产品名称
	ProductVersion	产品版本
	CompanyName	企业名称
	PrivateBuild	私有版本的信息
	SpecialBuild	特殊内部版本信息
	LegalTrademarks	文件的商标和注册商标
*/
FileGetInfo(FilePath:="", p*) {   ; Written by SKAN, modified by HotKeyIt	; www.autohotkey.com/forum/viewtopic.php?p=233188#233188  CD:24-Nov-2008 / LM:27-Oct-2010
	static DLL:="Version\GetFileVersion"
	If ! FSz := DllCall( DLL "InfoSize" (A_IsUnicode ? "W" : "A"), "Str",FilePath, "UInt",0 )
		Return DllCall( "SetLastError", UInt,1 ),""
	VarSetCapacity( FVI, FSz, 0 ),DllCall( DLL "Info" ( A_IsUnicode ? "W" : "A"), "Str",FilePath, "UInt",0, "UInt",FSz, "PTR",&FVI )
	If !DllCall( "Version\VerQueryValue" (A_IsUnicode ? "W" : "A"), "PTR",&FVI, "Str","\VarFileInfo\Translation", "PTR*",Transl, "PTR",0 )
		Return DllCall( "SetLastError", UInt,2 ),""
	If !Trans:=format("{1:.8X}",NumGet(Transl+0,"UInt"))
		Return DllCall( "SetLastError", UInt,3),""
	for k,v in p
	{
		subBlock := "\StringFileInfo\" SubStr(Trans,-3) SubStr(Trans,1,4) "\" v
		If ! DllCall( "Version\VerQueryValue" ( A_IsUnicode ? "W" : "A"), "PTR",&FVI, "Str",SubBlock, "PTR*",InfoPtr, "UInt",0 )
			continue
		If Value := StrGet( InfoPtr )
			Info .= p.MaxIndex()=1?Value:SubStr( v "                        ",1,24 ) . A_Tab . Value . "`n"
	}
	Info:=RTrim(Info,"`n")

	Return Info
}

GetProcessInfo(exename,type:="ExecutablePath") {
	/*
	class Win32_Process : CIM_Process
	{
		string   CreationClassName;	创建类的名称
		string   Caption;	进程标题
		string   CommandLine;	进程的命令行
		datetime CreationDate;	创建日期
		string   CSCreationClassName;
		string   CSName;
		string   Description;	显示进程说明
		string   ExecutablePath;	可执行路径
		uint16   ExecutionState;	执行状态
		string   Handle;	句柄
		uint32   HandleCount;	句柄统计
		datetime InstallDate;	安装日期
		uint64   KernelModeTime;	内核调节时间
		uint32   MaximumWorkingSetSize;
		uint32   MinimumWorkingSetSize;
		string   Name;	进程名
		string   OSCreationClassName;	系统创建类名
		string   OSName;	系统名
		uint64   OtherOperationCount;
		uint64   OtherTransferCount;
		uint32   PageFaults;	页面默认值
		uint32   PageFileUsage;
		uint32   ParentProcessId;
		uint32   PeakPageFileUsage;
		uint64   PeakVirtualSize;
		uint32   PeakWorkingSetSize;
		uint32   Priority = NULL;
		uint64   PrivatePageCount;
		uint32   ProcessId;	进程ID
		uint32   QuotaNonPagedPoolUsage;
		uint32   QuotaPagedPoolUsage;
		uint32   QuotaPeakNonPagedPoolUsage;
		uint32   QuotaPeakPagedPoolUsage;
		uint64   ReadOperationCount;
		uint64   ReadTransferCount;
		uint32   SessionId;	会话的唯一标识符
		string   Status;	状态值
		datetime TerminationDate;	终止日期
		uint32   ThreadCount;	线程计数
		uint64   UserModeTime;	用户模式时间
		uint64   VirtualSize;	虚拟化大小
		string   WindowsVersion;
		uint64   WorkingSetSize;
		uint64   WriteOperationCount;
		uint64   WriteTransferCount;
	};
	*/
	for process in ComObjGet("winmgmts:").ExecQuery("Select * from Win32_Process where name ='" exename "'")
		return process[type]
}
